package utils

import (
	"fmt"
	"github.com/gomodule/redigo/redis"
	"log"
	"lottery/comm"
	"lottery/datasource"
	"math"
	"time"
)

// 取2， 取模结果不是 0.就是 1. 分为两个片段
const ipFrameSize = 2

func init() {
	resetGroupIpList()
}

// 《redis 实战》 中降低内存占用： 这里简单的采用 分片操作， 分为两个。
// 这里将 ip 地址转换为 数字， 简单的实现了分两片
func IncrIpLuckNum(strIp string) int64 {
	// 转为数字
	ip := comm.Ip4toInt(strIp)
	//i := ip % ipFrameSize  // 位运算代替 取模运算，提升速度。
	i := ip & ipFrameSize
	key := fmt.Sprintf("day_ips_%d", i) // 确定在那个片段
	cacheObj := datasource.InstanceCache()
	// redis 是单线程的 ，没有并发的读写， 这些操作都可以看做是 原子操作。HINCRBY 进行递增
	rs, err := redis.Int64(cacheObj.Do("HINCRBY", key, ip, 1)) // 该命令当 key 在数据库中不存在的时候，会自动的创建
	if err != nil {
		log.Println("ip_day_lucky redis HINCRBY error = ", err.Error())
		return math.MaxInt32
	} else {
		// 不使用 redis.Int64,  这里进行 类型判断也行 re.(int64)
		return rs
	}
}

// 上述代码中的逻辑， key := fmt.Sprintf("day_ips_%d", i)
// 也就是说， key 的存储并没有区分时间， 所有的key都是存储在这里面的， 每天都存放在一起
// 需要做这样的一个更新处理， 到固定时间把 对应的键清理掉，删除当天的数据内容
func resetGroupIpList() {
	log.Println("ip_day_lucky.resetGroupIpList start")
	cacheObj := datasource.InstanceCache()
	// 清理掉缓存的内容
	// 遍历每个分片， 清除掉每个分段的内容
	for i := 0; i < ipFrameSize; i++ {
		// 先将对应的键更换别名（上面函数会自动创建新的功能）， 再做后序的删除工作
		// 避免了 迭代过程中， 又有增添等操作带来的各种问题
		key := fmt.Sprintf("day_ips_%d", i)
		cacheObj.Do("DEL", key) // del 清除就可以，不算大key
	}
	log.Println("ip_day_lucky.resetGroupIpList stop")
	// 设置计时器， 整点归零
	duration := comm.NextDayDuration()
	time.AfterFunc(duration, resetGroupIpList)
}
